package com.browseengine.bobo.facets.filter;

import java.io.IOException;

import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.OpenBitSet;

import com.browseengine.bobo.api.BoboSegmentReader;
import com.browseengine.bobo.docidset.EmptyDocIdSet;
import com.browseengine.bobo.docidset.RandomAccessDocIdSet;
import com.browseengine.bobo.facets.FacetHandler;
import com.browseengine.bobo.facets.data.FacetDataCache;
import com.browseengine.bobo.util.BigSegmentedArray;

public class FacetOrFilter extends RandomAccessFilter {
  protected final FacetHandler<FacetDataCache<?>> _facetHandler;
  protected final String[] _vals;
  private final boolean _takeCompliment;
  private final FacetValueConverter _valueConverter;

  public FacetOrFilter(FacetHandler<FacetDataCache<?>> facetHandler, String[] vals) {
    this(facetHandler, vals, false);
  }

  public FacetOrFilter(FacetHandler<FacetDataCache<?>> facetHandler, String[] vals,
      boolean takeCompliment) {
    this(facetHandler, vals, takeCompliment, FacetValueConverter.DEFAULT);
  }

  public FacetOrFilter(FacetHandler<FacetDataCache<?>> facetHandler, String[] vals,
      boolean takeCompliment, FacetValueConverter valueConverter) {
    _facetHandler = facetHandler;
    _vals = vals;
    _takeCompliment = takeCompliment;
    _valueConverter = valueConverter;
  }

  @Override
  public double getFacetSelectivity(BoboSegmentReader reader) {
    double selectivity = 0;
    FacetDataCache<?> dataCache = _facetHandler.getFacetData(reader);
    int accumFreq = 0;
    for (String value : _vals) {
      int idx = dataCache.valArray.indexOf(value);
      if (idx < 0) {
        continue;
      }
      accumFreq += dataCache.freqs[idx];
    }
    int total = reader.maxDoc();
    selectivity = (double) accumFreq / (double) total;
    if (selectivity > 0.999) {
      selectivity = 1.0;
    }
    if (_takeCompliment) {
      selectivity = 1.0 - selectivity;
    }
    return selectivity;
  }

  @Override
  public RandomAccessDocIdSet getRandomAccessDocIdSet(BoboSegmentReader reader) throws IOException {
    if (_vals.length == 0) {
      return EmptyDocIdSet.getInstance();
    } else {
      return new FacetOrRandomAccessDocIdSet(_facetHandler, reader, _vals, _valueConverter,
          _takeCompliment);
    }
  }

  public static class FacetOrRandomAccessDocIdSet extends RandomAccessDocIdSet {

    private final OpenBitSet _bitset;
    private final BigSegmentedArray _orderArray;
    private final FacetDataCache<?> _dataCache;
    private final int[] _index;

    @SuppressWarnings("unchecked")
    FacetOrRandomAccessDocIdSet(FacetHandler<FacetDataCache<?>> facetHandler,
        BoboSegmentReader reader, String[] vals, FacetValueConverter valConverter,
        boolean takeCompliment) {
      _dataCache = facetHandler.getFacetData(reader);
      _orderArray = _dataCache.orderArray;
      _index = valConverter.convert(((FacetDataCache<String>) _dataCache), vals);

      _bitset = new OpenBitSet(_dataCache.valArray.size());
      for (int i : _index) {
        _bitset.fastSet(i);
      }

      if (takeCompliment) {
        // flip the bits
        for (int i = 0; i < _dataCache.valArray.size(); ++i) {
          _bitset.fastFlip(i);
        }
      }
    }

    @Override
    public boolean get(int docId) {
      return _bitset.fastGet(_orderArray.get(docId));
    }

    @Override
    public DocIdSetIterator iterator() throws IOException {
      return new FacetOrDocIdSetIterator(_dataCache, _bitset);
    }

  }

  public static class FacetOrDocIdSetIterator extends DocIdSetIterator {
    protected int _doc;
    protected final FacetDataCache<?> _dataCache;
    protected int _maxID;
    protected final OpenBitSet _bitset;
    protected final BigSegmentedArray _orderArray;

    public FacetOrDocIdSetIterator(FacetDataCache<?> dataCache, OpenBitSet bitset) {
      _dataCache = dataCache;
      _orderArray = dataCache.orderArray;
      _bitset = bitset;

      _doc = Integer.MAX_VALUE;
      _maxID = -1;
      int size = _dataCache.valArray.size();
      for (int i = 0; i < size; ++i) {
        if (!bitset.fastGet(i)) {
          continue;
        }
        if (_doc > _dataCache.minIDs[i]) {
          _doc = _dataCache.minIDs[i];
        }
        if (_maxID < _dataCache.maxIDs[i]) {
          _maxID = _dataCache.maxIDs[i];
        }
      }
      _doc--;
      if (_doc < 0) _doc = -1;
    }

    @Override
    final public int docID() {
      return _doc;
    }

    @Override
    public int nextDoc() throws IOException {
      return (_doc = (_doc < _maxID ? _orderArray.findValues(_bitset, (_doc + 1), _maxID)
          : NO_MORE_DOCS));
    }

    @Override
    public int advance(int id) throws IOException {
      if (_doc < id) {
        return (_doc = (id <= _maxID ? _orderArray.findValues(_bitset, id, _maxID) : NO_MORE_DOCS));
      }
      return nextDoc();
    }

    @Override
    public long cost() {
      // TODO Auto-generated method stub
      return 0;
    }
  }

}
